package com.disruptor.controller;

import com.disruptor.util.HelpMe;
import com.jfinal.plugin.activerecord.Db;
import com.jfinal.plugin.activerecord.IAtom;
import com.jfinal.plugin.activerecord.Record;
import com.xiaoleilu.hutool.date.DateUtil;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;

import java.sql.SQLException;
import java.util.List;

@Controller
@RequestMapping("/tx")
/**
 * mysql锁机制分为表级锁和行级锁，本文就和大家分享一下我对mysql中行级锁中的共享锁与排他锁进行分享交流。

 共享锁又称为读锁，简称S锁，顾名思义，共享锁就是多个事务对于同一数据可以共享一把锁，都能访问到数据，但是只能读不能修改。

 排他锁又称为写锁，简称X锁，顾名思义，排他锁就是不能与其他所并存，如一个事务获取了一个数据行的排他锁，其他事务就不能再获取该行的其他锁，
 包括共享锁和排他锁，但是获取排他锁的事务是可以对数据就行读取和修改。

 对于共享锁大家可能很好理解，就是多个事务只能读数据不能改数据，对于排他锁大家的理解可能就有些差别，
 我当初就犯了一个错误，以为排他锁锁住一行数据后，其他事务就不能读取和修改该行数据，其实不是这样的。
 排他锁指的是一个事务在一行数据加上排他锁后，其他事务不能再在其上加其他的锁。
 mysql InnoDB引擎默认的修改数据语句，update,delete,insert都会自动给涉及到的数据加上排他锁，
 select语句默认不会加任何锁类型，如果加排他锁可以使用select ...for update语句，加共享锁可以使用select ... lock in share mode语句。
 所以加过排他锁的数据行在其他事务种是不能修改数据的，也不能通过for update和lock in share mode锁的方式查询数据，
 但可以直接通过select ...from...查询数据，因为普通查询没有任何锁机制。

 */
public class TxTestController {

	private Logger log = LoggerFactory.getLogger(getClass());

	@RequestMapping("/test")
	@ResponseBody
	public String test1(){
		log.info("test ......");

		new Thread(new Runnable() {
			@Override
			public void run() {
				log.info(Thread.currentThread().getName());

				boolean succeed = Db.tx(new IAtom() {
					public boolean run() throws SQLException {


//						mysql 排它锁		http://www.cnblogs.com/boblogsbo/p/5602122.html
						List<Record> recordList = Db.find("select * from test where status = 1 order by id limit 4 for update");

						try {
							Thread.sleep(1000*3);
						} catch (InterruptedException e) {
							e.printStackTrace();
						}


						if (HelpMe.isNotNull(recordList)){

							List<String> idList = HelpMe.filterRecordList(recordList, String.class, "id");
							for (String id:idList){
								String sql = "update test set status = 2 where id = '"+id+"'";
								System.out.println(sql);
								Db.update(sql);
							}
						}

						return true;
					}
				});

			}
		},"thread-1").start();


		new Thread(new Runnable() {
			@Override
			public void run() {
				log.info(Thread.currentThread().getName());

				boolean succeed = Db.tx(new IAtom() {
					public boolean run() throws SQLException {

						System.out.println("当前时间："+DateUtil.now());

						//如果之前有事务通过 for update 的方式查询，则此处的 for update 查询会被阻塞，直到前一个事务提交或回滚，但是普通的 select 查询不受 for update 的影响
						List<Record> recordList = Db.find("select * from test where status = 1 order by id limit 4 for update");

						System.out.println("当前时间："+DateUtil.now());

						if (HelpMe.isNotNull(recordList)){

							List<String> idList = HelpMe.filterRecordList(recordList, String.class, "id");
							for (String id:idList){
								String sql = "update test set status = 3 where id = '"+id+"'";
								System.out.println(sql);
								Db.update(sql);
							}
						}

						return true;
					}
				});

			}
		},"thread-2").start();


		return "test";
	}







}



















